Dart has built-in support for functions, meaning that they can
be assigned to variables like any other type.
However, this leads to two ways of defining functions:
- function declarations, both in top-level and nested scopes
- and function expressions assigned to variables, which can be library-level variables or local ones
The second way allows to redefine the function behavior after first assignment, by simply reassigning the variable to a new function expression.
When this is not the case, the function expression should be replaced with a function declaration, to make the code more readable and consistent.
Exceptions
Top-level variables are allowed, since they can change their value over time.
typedef StringRetriever = String Function(Map<String, String> data);
StringRetriever currentRetriever = (data) => '';
However, top-level final
variables are not allowed, since their value cannot change once initialized. Therefore, there is no reason
not to define them as function expressions.
final StringRetriever currentRetriever = (data) => ''; // Noncompliant
Nested function variables, on the other hand, are always considered non-compliant, whether they are final
or not, but only if their
values is not mutated.
void main() {
StringRetriever mutableRetriever = (data) => ''; // Noncompliant
final StringRetriever immutableRetriever = (data) => ''; // Noncompliant
var nestedFunctionVariable = () { }; // Compliant
nestedFunctionVariable = () { print('Hi'); };
}
The same applies to class fields (instance and static):
class AClass {
final StringRetriever immutableFunctionField = (data) => ''; // Noncompliant
StringRetriever mutableFunctionField = (data) => ''; // Compliant
static final StringRetriever immutableStaticFunctionField = (data) => ''; // Noncompliant
static StringRetriever mutableStaticFunctionField = (data) => ''; // Compliant
}